#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <strings.h>
#include <unistd.h>

int main()
{
    // 1. 创建套接字
    int serverfd = socket(AF_INET, SOCK_STREAM, 0);
    if (serverfd < 0) {
        perror("socket failed");
        return -1;
    }

    // 2. 设置端口复用
    int opt = 1;
    int n = setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if (n < 0) {
        perror("setsockopt failed");
        close(serverfd);
        return -2;
    }

    // 3. 绑定
    struct sockaddr_in addr;
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY; 
    addr.sin_port = htons(8080);

    n = bind(serverfd, (struct sockaddr*)&addr, sizeof(addr));
    if (n < 0) {
        perror("bind failed");
        close(serverfd);
        return -3;
    }

    // 4. 开始监听
    n = listen(serverfd, 8);
    if (n < 0) {
        perror("listen failed");
        close(serverfd);
        return -4;
    }

    // 5. 多路复用
    fd_set master;
    FD_ZERO(&master);
    FD_SET(serverfd, &master);

    int maxfd = serverfd;

    while (1) {
        fd_set working = master; 

        // important

        // 6. 等待新事件
        n = select(maxfd + 1, &working, NULL, NULL, NULL);
        if (n < 0) {
            perror("select failed");
            close(serverfd);
            return -5;
        }

        // 7. 检查新连接
        if (FD_ISSET(serverfd, &working)) {

            struct sockaddr_in peer;
            bzero(&peer, sizeof(peer));
            socklen_t len = sizeof(peer);

            int clientfd = accept(serverfd, (struct sockaddr*)&peer, &len);
            if (clientfd < 0) {
                perror("accept failed");
                close(serverfd);
                return -6;
            }

            FD_SET(clientfd, &master);
            if (clientfd > maxfd) {
                maxfd = clientfd;
            }
        }

        // 8. 遍历处理 IO 任务
        char buffer[128] = {0};
        for (int i = serverfd + 1; i < maxfd + 1; i++) {
            
            // 9. 检查 i 是否在 working 中
            if (FD_ISSET(i, &working)) {
                int cnt = recv(i, buffer, 128, 0);
                buffer[cnt] = 0;

                // <= 0 要同时判断
                if (cnt <= 0) {
                    // 从 master 中移除
                    FD_CLR(i, &master);
                    // 关闭文件描述符
                    close(i);
                }
                else {

                    printf("clientfd: %d, count: %d, buffer: %s\n", i, cnt, buffer);
                    // 10. 回显
                    send(i, buffer, cnt, 0);
                }
            }
        }
    }

    close(serverfd);

    return 0;
}